﻿
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using SharedUtils;
using System.Collections;

namespace GraphEditor
{
    public partial class GraphEditorForm : UserControl
    {
        // Specific classes for some of the WinForm elements
        private class ListItem
        {
            public string Text { get; set; }
            public object Value { get; set; }

            public override string ToString()
            {
                return Text;
            }
        }

        // Fields
        private Util utils;
        private Node currentNode;
        private Edge<Node, EdgeData> currentEdge;
        private Entity currentEntity;
        private Graph<Node, EdgeData> graph;
        private ToolStripStatusLabel label;


        // HACK VARIABLES
        private bool updateCheckBoxes, edgeEntityOverride, cameraOverride, transitionOverride;
        private InputType.InputName defaultType;

        // Constructor
        public GraphEditorForm(ToolStripStatusLabel label, Util utils, Graph<Node, EdgeData> graph)
        {
            //HACK VARIABLES
            updateCheckBoxes = true;
            defaultType = InputType.InputName.Timed;
            edgeEntityOverride = cameraOverride = transitionOverride = false;

            InitializeComponent();

            // Get the utils from the editor
            this.utils = utils;

            // Initialize the Graph
            this.graph = graph;

            // THE ALL IMPORTANT LABEL
            this.label = label;

            // Make the root node
            graph.Nodes.Add(new Node("Root"));
            graph.Nodes[0].entities = CopyEntities(utils.entities);
            graph.Nodes[0].camera = utils.defaultCamera;

            // Hook up the Node list
            populateNodeList();

            // Initialize the root node to show its properties
            setCurrentNode(graph.Nodes[0]);
        }

        public void populateCameraList()
        {
            if (utils.cameras != null)
            {
                ListItem cameraItem;
                for (int i = 0; i < utils.cameras.Count; i++)
                {
                    cameraItem = new ListItem();
                    cameraItem.Text = utils.cameras[i].name;
                    cameraItem.Value = utils.cameras[i];
                    this.cameraDropDown.Items.Add(cameraItem);
                }
            }
        }

        // Makes a copy of an entity list
        public List<Entity> CopyEntities(List<Entity> entities)
        {
            List<Entity> answer = new List<Entity>();
            for (int i = 0; i < entities.Count; i++)
            {
                // Make a copy of that entity
                if (entities[i] is SoundEntity)
                    answer.Add(new SoundEntity((SoundEntity)entities[i]));
                else if (entities[i] is PictureEntity)
                    answer.Add(new PictureEntity((PictureEntity)entities[i]));
                else
                    answer.Add(new Entity(entities[i]));
            }

            return answer;
        }

        // Populates the node list
        public void populateNodeList()
        {
            // First, since we will call this all the time, clear the list and repopulate it from
            // the new list of nodes
            NodeList.Items.Clear();


            // Then, we must put them in the list box
            foreach (Node n in graph.Nodes)
            {
                ListItem item = new ListItem();
                item.Text = n.ToString();
                item.Value = n;
                this.NodeList.Items.Add(item);
            }
        }

        // Given the node, it will populate the Entity and Edge lists
        private void setCurrentNode(Node n)
        {
            // Update current node
            currentNode = n;

            // Make sure that the GUI reflects it is chosen
            NodeList.SelectedIndex = graph.Nodes.IndexOf(currentNode);

            // First of all, we must clear the list of entites and edges
            EntityList.Items.Clear();
            EdgeList.Items.Clear();

            // Populate the Entity List Box with the entities inside of the node
            foreach (Entity e in n.entities)
            {
                ListItem item = new ListItem();
                item.Text = e.ToString();
                item.Value = e;
                this.EntityList.Items.Add(item);
            }

            // Populate the Edge list with the outgoing edges from this node
            foreach (Edge<Node, EdgeData> e in graph.GetEdgesFrom(n))
            {
                ListItem item = new ListItem();
                item.Text = e.ToString();
                item.Value = e;
                this.EdgeList.Items.Add(item);
            }

            // Set camera of the node for the list
            if (cameraDropDown.Items.Count == 0)
            {
                // Hook up camera objects
                populateCameraList();
            }

            // Select the camera that the node actually has
            int cameraIndex = utils.cameras.IndexOf(currentNode.camera);
            cameraDropDown.SelectedIndex = cameraIndex;

            // Select the first entity
            //EntityList.SelectedIndex = 0;

            // Chnage the label to reflect this change
            changeLabel("Selected Node: " + currentNode);
        }

        // ADD AN EDGE
        private void button5_Click(object sender, EventArgs e)
        {
            // Do nothing if there is no selected item
            // TODO: Add an error message
            if (currentNode == null) return;

            // Making every node originally point back to the root
            Edge<Node, EdgeData> edgeToAdd = new Edge<Node, EdgeData>(currentNode, graph.Nodes[0], new EdgeData());

            // Try and find a possible edge to add (there has to be an open node to go to)
            bool foundANode = false;
            bool copyOfEdge = false;
            foreach (Node n in graph.Nodes)
            {
                foundANode = true;
                edgeToAdd.End = n;
                foreach (Edge<Node, EdgeData> ed2 in graph.GetEdgesFrom(currentNode))
                {
                    if (ed2.End == edgeToAdd.End && ed2.Start == edgeToAdd.Start)
                    {
                        foundANode = false;
                        break;
                    }
                }

                if (foundANode)
                    break;
            }

            // Find a target that is available
            int targetIndex = 0;
            List<Edge<Node, EdgeData>> edges = graph.GetEdgesFrom(currentNode).ToList();
            for (int i = 0; i < edges.Count; i++)
            {
                if (targetIndex == 0)
                {
                    if (edges[i].Data.ent == null)
                    {
                        targetIndex++;
                        if (targetIndex > currentNode.entities.ToList().Count)
                        {
                            copyOfEdge = true;
                            break;
                        }
                    }
                    continue;
                }

                else if (currentNode.entities.ToList()[targetIndex - 1] == edges[i].Data.ent)
                {
                    targetIndex++;
                    if (targetIndex > currentNode.entities.ToList().Count)
                    {
                        copyOfEdge = true;
                        break;
                    }
                }

            }

            if (!foundANode || copyOfEdge)
            {
                //TO DO: ADD AN ERROR MESSAGE: CANNOT ADD AN EDGE BECAUSE ONE EXISTS FOR ITS PURPOSE
                MessageBox.Show("There can be no more edges. A node must be added before another edge.", "Duplicate Edge", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            // Add default camera parameters if needed
            if (edgeToAdd.Start.camera != edgeToAdd.End.camera)
            {
                edgeToAdd.Data.cameraType = Interpolation.InterpolationMode.LINEAR;
                edgeToAdd.Data.cameraTime = 1;
            }

            graph.AddEdge(edgeToAdd);
            setCurrentNode(currentNode);
            setCurrentEdge(edgeToAdd);

            //edgeToAdd.Data.ent = targetIndex == 0 ? null : currentNode.entities.ToList()[targetIndex - 1];

            if (targetIndex == 0)
            {
                edgeToAdd.Data.ent = null;

                // Add a default input type of TIMED  
                addEvent(InputType.InputName.Timed);
            }
            else
            {
                // Add a default input type of TAP 
                addEvent(InputType.InputName.Tap);
                edgeToAdd.Data.ent = currentNode.entities.ToList()[targetIndex - 1];
            }


            RefreshEdgeProperties();
            RefreshTransitionProperties();

            // Set Destination Node in the GUI element
            destinationNode.SelectedIndex = 0;

            // Set the entity that you want in the GUI element
            if (currentEdge.Data.ent == null)
                edgeEntityList.SelectedIndex = 0;
        }

        private void NodeList_SelectedIndexChanged(object sender, EventArgs e)
        {
            // On a selected item change, run the methods appropriate to change the
            // the elements of entities and edges
            edgeEntityOverride = true;

            if (NodeList.SelectedIndex >= 0)
                setCurrentNode(graph.Nodes[NodeList.SelectedIndex]);

            // Select the first edge (IF IT IS THERE)
            if (graph.GetEdgesFrom(currentNode).ToList().Count > 0)
                setCurrentEdge(graph.GetEdgesFrom(currentNode).ToList()[0]);
            else
                setCurrentEdge(null);

            edgeEntityOverride = false;

        }

        private void cameraDropDown_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Update the camera in the current node
            if (this.cameraDropDown.SelectedIndex >= 0)
                currentNode.camera = utils.cameras[this.cameraDropDown.SelectedIndex];

            for (int i = 0; i < graph.Nodes.Count; i++)
            {
                Node n = graph.Nodes[i];
                List<Edge<Node, EdgeData>> eds = graph.GetEdgesFrom(n).ToList();
                for (int j = 0; j < eds.Count; j++)
                {
                    Edge<Node, EdgeData> edge = eds[j];
                    if (edge.End.camera != edge.Start.camera && edge.Data.cameraType == Interpolation.InterpolationMode.NONE)
                    {
                        edge.Data.cameraTime = 1;
                        edge.Data.cameraType = Interpolation.InterpolationMode.LINEAR;
                    }
                }
            }
        }

        // ADD NODE BUTTON
        private void button2_Click(object sender, EventArgs e)
        {
            Node toAdd = new Node("Node " + (graph.Nodes.Count + 1));
            toAdd.camera = utils.defaultCamera;
            toAdd.entities = CopyEntities(utils.entities);
            // we are going to add a fresh node (you can copy the root to get that if you want it)
            for (int i = 0; i < toAdd.entities.Count; i++)
            {
                toAdd.entities[i].isVisible = true;
            }
            currentNode = toAdd;

            using (NameInput dialog = new NameInput(toAdd))
            {
                DialogResult result = dialog.ShowDialog();
            }

            // DO NOT ALLOW BLANK NAMES
            if (toAdd.name.Trim().Equals(""))
            {
                MessageBox.Show("Enter a name for the node.", "Invalid Name", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            // CHECK TO SEE IF WE ALREADY HAVE A NODE WITH THAT NAME
            for (int i = 0; i < graph.Nodes.Count; i++)
            {
                Node n = graph.Nodes[i];
                if (n.name.Equals(toAdd.name))
                {
                    // HERE IS WHERE WE MAKE A POPUP ERROR BOX
                    MessageBox.Show("A node with that name already exists.", "Invalid Name", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
            }

            graph.Add(toAdd);

            // Rebuild the list
            populateNodeList();

            // Select the node from the list
            NodeList.SelectedIndex = graph.Nodes.Count - 1;
            setCurrentNode(toAdd);

            // Rephresh the edges
            RefreshEdgeProperties();
        }

        // DELETE NODE BUTTON
        private void button1_Click(object sender, EventArgs e)
        {
            // Do nothing if you try to delete the root
            if (NodeList.SelectedIndex <= 0)
            {
                MessageBox.Show("Cannot delete the root node. The root node is needed to start the graph. The values for the entities for the root node are initialized as detailed in the scene editor.", "Invalid Request", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            // Remove it
            graph.Remove((Node)((ListItem)NodeList.Items[NodeList.SelectedIndex]).Value);

            // Repopulate to update the GUI
            populateNodeList();
            setCurrentNode(graph.Nodes[0]);
            NodeList.SelectedIndex = 0;
        }

        // COPY NODE BUTTON
        private void button3_Click(object sender, EventArgs e)
        {
            // Create a new node with a fresh copy of a new entity list from the root
            Node toAdd = new Node("Node " + (graph.Nodes.Count + 1));
            toAdd.camera = currentNode.camera;
            toAdd.entities = CopyEntities(currentNode.entities);
            currentNode = toAdd;

            using (NameInput dialog = new NameInput(toAdd))
            {
                DialogResult result = dialog.ShowDialog();
            }

            // DO NOT ALLOW BLANK NAMES
            if (toAdd.name.Trim().Equals(""))
            {
                MessageBox.Show("Enter a name for the node.", "Invalid Name", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            // CHECK TO SEE IF WE ALREADY HAVE A NODE WITH THAT NAME
            for (int i = 0; i < graph.Nodes.Count; i++)
            {
                Node n = graph.Nodes[i];
                if (n.name.Equals(toAdd.name))
                {
                    // HERE IS WHERE WE MAKE A POPUP ERROR BOX
                    MessageBox.Show("A node with that name already exists.", "Invalid Name", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
            }

            graph.Add(toAdd);

            // Rebuild the list
            populateNodeList();

            // Select the node from the list
            NodeList.SelectedIndex = graph.Nodes.Count - 1;
            setCurrentNode(toAdd);

            // Rephresh the edges
            RefreshEdgeProperties();
        }

        private void EntityList_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (EntityList.SelectedIndex == -1) return;

            // Get the entity that was chosen
            currentEntity = currentNode.entities[EntityList.SelectedIndex];

            // Alter the list of options for visibility
            if (cameraOptions.Items.Count == 0)
            {
                ListItem item = new ListItem();
                item.Text = "Visible";
                item.Value = true;
                cameraOptions.Items.Add(item);
                item = new ListItem();
                item.Text = "Not Visible";
                item.Value = false;
                cameraOptions.Items.Add(item);
            }

            if (currentEntity.isVisible)
                cameraOptions.SelectedIndex = 0;
            else
                cameraOptions.SelectedIndex = 1;


            // handle the sound options list
            if (currentEntity is SoundEntity)
            {
                soundOptions.Enabled = true;
                // set up/alter the sound options if its a sound entity
                if (soundOptions.Items.Count == 0)
                {
                    ListItem item = new ListItem();
                    item = new ListItem();
                    item.Text = "Play/Resume";
                    item.Value = SoundEntity.SoundEntityState.PLAYING;
                    soundOptions.Items.Add(item);
                    item = new ListItem();
                    item.Text = "Pause";
                    item.Value = SoundEntity.SoundEntityState.PAUSED;
                    soundOptions.Items.Add(item);
                    item = new ListItem();
                    item.Text = "Stop";
                    item.Value = SoundEntity.SoundEntityState.STOPPED;
                    soundOptions.Items.Add(item);
                }

                for (int i = 0; i < soundOptions.Items.Count; i++)
                {
                    if ((SoundEntity.SoundEntityState)((ListItem)soundOptions.Items[i]).Value == ((SoundEntity)currentEntity).state)
                        soundOptions.SelectedIndex = i;
                }

            }
            else
            {
                soundOptions.Enabled = false;
                soundOptions.SelectedIndex = -1;
            }

            changeLabel("Selected entity " + currentEntity.ToString());
        }

        private void setCurrentEdge(Edge<Node, EdgeData> e)
        {
            currentEdge = e;

            // Make the GUI highlight this element
            if (e != null)
                EdgeList.SelectedIndex = graph.GetEdgesFrom(currentNode).ToList().IndexOf(currentEdge);

            // Update the GUI lists
            RefreshEdgeProperties();
            RefreshTransitionProperties();

            // Update the label
            changeLabel("Selected Edge: " + currentEdge);
        }


        private void cameraOptions_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (cameraOverride) return;

            currentEntity.isVisible = cameraOptions.SelectedIndex == 0;

            // Change the name of the thing that got changed
            ListItem item = new ListItem();
            item.Text = currentEntity.ToString();
            item.Value = currentEntity;
            EntityList.Items[EntityList.SelectedIndex] = item;
        }

        private void EdgeList_SelectedIndexChanged(object sender, EventArgs e)
        {
            edgeEntityOverride = true;
            if (EdgeList.SelectedIndex >= 0)
                setCurrentEdge(graph.GetEdgesFrom(currentNode).ToList()[EdgeList.SelectedIndex]);
            edgeEntityOverride = false;
        }


        // REFRESHES THE GUI of the Transition Properties (camera and time)
        private void RefreshTransitionProperties()
        {
            if (currentEdge == null)
                this.transitionPropertiesBox.Enabled = false;
            else
                this.transitionPropertiesBox.Enabled = currentEdge.End.camera != currentEdge.Start.camera;

            // Do nothing if this control is not enabled
            if (!this.transitionPropertiesBox.Enabled)
            {
                transitionTypeDropDown.SelectedIndex = -1;
                transitionTypeDropDown.Items.Clear();
                cameraTimeframe.Text = "";
                return;
            }

            // Add the transition drop down stuff if it isnt populated
            if (this.transitionTypeDropDown.Items.Count == 0)
            {
                ListItem item = new ListItem();
                item.Text = "Linear";
                item.Value = Interpolation.InterpolationMode.LINEAR;
                transitionTypeDropDown.Items.Add(item);

                item = new ListItem();
                item.Text = "Sigmoid";
                item.Value = Interpolation.InterpolationMode.SIGMOID;
                transitionTypeDropDown.Items.Add(item);


                item = new ListItem();
                item.Text = "Cubic Ease In";
                item.Value = Interpolation.InterpolationMode.CUBIC_EASEIN;
                transitionTypeDropDown.Items.Add(item);


                item = new ListItem();
                item.Text = "Bounce Ease Out";
                item.Value = Interpolation.InterpolationMode.BOUNCE_EASEOUT;
                transitionTypeDropDown.Items.Add(item);


                item = new ListItem();
                item.Text = "Elastic Ease Out";
                item.Value = Interpolation.InterpolationMode.ELASTIC_EASEOUT;
                transitionTypeDropDown.Items.Add(item);


                item = new ListItem();
                item.Text = "Sine Ease In";
                item.Value = Interpolation.InterpolationMode.SINE_EASEIN;
                transitionTypeDropDown.Items.Add(item);

                item = new ListItem();
                item.Text = "Sine Ease Out";
                item.Value = Interpolation.InterpolationMode.SINE_EASEOUT;
                transitionTypeDropDown.Items.Add(item);

            }
            transitionOverride = true;
            switch (currentEdge.Data.cameraType)
            {
                case Interpolation.InterpolationMode.LINEAR:
                    transitionTypeDropDown.SelectedIndex = 0;
                    break;
                case Interpolation.InterpolationMode.SIGMOID:
                    transitionTypeDropDown.SelectedIndex = 1;
                    break;
                case Interpolation.InterpolationMode.CUBIC_EASEIN:
                    transitionTypeDropDown.SelectedIndex = 2;
                    break;
                case Interpolation.InterpolationMode.BOUNCE_EASEOUT:
                    transitionTypeDropDown.SelectedIndex = 3;
                    break;
                case Interpolation.InterpolationMode.ELASTIC_EASEOUT:
                    transitionTypeDropDown.SelectedIndex = 4;
                    break;
                case Interpolation.InterpolationMode.SINE_EASEIN:
                    transitionTypeDropDown.SelectedIndex = 5;
                    break;
                case Interpolation.InterpolationMode.SINE_EASEOUT:
                    transitionTypeDropDown.SelectedIndex = 6;
                    break;
            }
            transitionOverride = false;
            cameraTimeframe.Text = currentEdge.Data.cameraTime.ToString();
        }

        // REFRESH GUI of Edges (NOT from the edgeList, but edgeEntityList, destinationNode, etc)
        private void RefreshEdgeProperties()
        {
            // If there is no edge, make sure everything is cleared!
            if (currentEdge == null)
            {
                destinationNode.SelectedIndex = -1;
                edgeEntityList.SelectedIndex = -1;

                // Clear the checkboxes and the text boxes that go in there
                clearAllChecks();

                // Then turn everything here off
                edgePropertiesBox.Enabled = false;
                eventTypes.Enabled = false;

                // Do nothing else if there is no edge selected
                return;
            }

            // Enable the groups that this represents
            edgePropertiesBox.Enabled = true;
            eventTypes.Enabled = true;

            // Clear the destination node list and add new elements 
            destinationNode.Items.Clear();

            // Refresh the values in the destination node drop down list
            for (int i = 0; i < graph.Nodes.Count; i++)
            {
                ListItem item = new ListItem();
                item.Text = graph.Nodes[i].name;
                item.Value = graph.Nodes[i];
                destinationNode.Items.Add(item);
            }

            // Choose the correct destination node from the list automatically
            destinationNode.SelectedIndex = graph.Nodes.IndexOf(currentEdge.End);

            // Fill in the entity list if its the first time
            if (edgeEntityList.Items.Count <= 0)
            {
                ListItem item = new ListItem();
                item.Text = "No Target";
                item.Value = null;
                edgeEntityList.Items.Add(item);

                for (int i = 0; i < currentNode.entities.Count; i++)
                {
                    item = new ListItem();
                    item.Text = currentNode.entities[i].name;
                    item.Value = currentNode.entities[i];
                    edgeEntityList.Items.Add(item);
                }
            }

            // Choose the correct entity target given the current edge
            if (currentEdge.Data.ent == null)
                edgeEntityList.SelectedIndex = 0;
            else
                edgeEntityList.SelectedIndex = currentNode.entities.IndexOf(currentEdge.Data.ent) + 1;

            // In order to have the correct checks, clear them all at first
            clearAllChecks();

            // Then check the ones that we know it has
            updateCheckBoxes = false;
            for (int i = 0; i < currentEdge.Data.inputTypes.Count; i++)
            {
                InputType.InputName eventToAdd = currentEdge.Data.inputTypes[i].inputName;

                if (eventToAdd == InputType.InputName.Tap)
                {
                    tapEvent.Checked = true;
                    tapEvent.CheckState = CheckState.Checked;
                }
                else if (eventToAdd == InputType.InputName.DoubleTap)
                {
                    doubleTapEvent.Checked = true;
                    doubleTapEvent.CheckState = CheckState.Checked;
                }
                else if (eventToAdd == InputType.InputName.SwipeLeft)
                {
                    swipeEvent.Checked = true;
                    swipeEvent.CheckState = CheckState.Checked;
                    swipeLeft.Checked = true;
                    swipeLeft.CheckState = CheckState.Checked;
                }
                else if (eventToAdd == InputType.InputName.SwipeRight)
                {
                    swipeEvent.Checked = true;
                    swipeEvent.CheckState = CheckState.Checked;
                    swipeRight.Checked = true;
                    swipeRight.CheckState = CheckState.Checked;
                }
                else if (eventToAdd == InputType.InputName.SwipeUp)
                {
                    swipeEvent.Checked = true;
                    swipeEvent.CheckState = CheckState.Checked;
                    swipeUp.Checked = true;
                    swipeUp.CheckState = CheckState.Checked;
                }
                else if (eventToAdd == InputType.InputName.SwipeDown)
                {
                    swipeEvent.Checked = true;
                    swipeEvent.CheckState = CheckState.Checked;
                    swipeDown.Checked = true;
                    swipeDown.CheckState = CheckState.Checked;
                }
                else if (eventToAdd == InputType.InputName.Hold)
                {
                    holdEvent.Checked = true;
                    holdEvent.CheckState = CheckState.Checked;
                    holdTime.Text = currentEdge.Data.inputTypes[i].deltaTime.ToString();
                    holdTime.Enabled = true;
                }
                else if (eventToAdd == InputType.InputName.Timed)
                {
                    timedEvent.Checked = true;
                    timedEvent.CheckState = CheckState.Checked;
                    timedEventTime.Text = currentEdge.Data.inputTypes[i].deltaTime.ToString();
                    timedEventTime.Enabled = true;
                }
            }
            updateCheckBoxes = true;

            // Enable the camera properties and the transition type if cameras are different  when the cameras of the two nodes do not match!
            this.transitionPropertiesBox.Enabled = this.transitionTypeDropDown.Enabled = currentEdge.Start.camera != currentEdge.End.camera;

            // Enable all the checkboxes if swipe is checked
            swipeTypePanel.Enabled = swipeEvent.Checked && !NoSwipeChecked();

            // Refresh the GUI
            RefreshTransitionProperties();
        }

        // REMOVE EDGE
        private void button4_Click(object sender, EventArgs e)
        {
            if (EdgeList.SelectedIndex < 0)
            {
                MessageBox.Show("There are no edges to delete.", "Invalid Request", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            Edge<Node, EdgeData> toRemove = graph.getEdgeAtIndex(currentNode, EdgeList.SelectedIndex);
            graph.RemoveEdge(currentNode, toRemove.End);
            setCurrentNode(currentNode);

            // CLEANUP THE UI. Select the first edge of the current node on a delete OR if no edges exist, make the entire list refresh to nothing.
            if (graph.GetEdgesFrom(currentNode).ToList().Count > 0)
                setCurrentEdge(graph.GetEdgesFrom(currentNode).ToList()[0]);
            else
            {
                currentEdge = null;
                RefreshEdgeProperties();
            }
        }

        private void edgeEntityList_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (edgeEntityOverride) return;

            // here is a check to see if there exists an edge like this
            // ********ERROR PREVENTION ********
            // ********DO NOT ALLOW 2 EDGES WITH SAME ACTION AND SAME TARGET********
            List<Edge<Node, EdgeData>> edges = graph.GetEdgesFrom(currentNode).ToList();
            for (int i = 0; i < edges.Count; i++)
            {
                // Its ok if its the edge we are on right now
                if (edges[i] == currentEdge) continue;

                // Here is the check:
                // 1. Targets must be the same
                if (edgeEntityList.SelectedIndex == 0)
                {
                    if (edges[i].Data.ent == null)
                    {
                        edgeEntityOverride = true;
                        edgeEntityList.SelectedIndex = currentNode.entities.IndexOf(currentEdge.Data.ent) + 1;
                        edgeEntityOverride = false;
                        MessageBox.Show("An edge with the same target entity already exists. The edge is:" + System.Environment.NewLine + edges[i].ToString(), "Duplicate Edge", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        return;
                    }
                }
                else if (edges[i].Data.ent == currentNode.entities.ToList()[edgeEntityList.SelectedIndex - 1])
                {
                    // Reverse the decision, then stop
                    // TODO: OPEN A DIALOG TO MAKE ERROR
                    edgeEntityOverride = true;
                    edgeEntityList.SelectedIndex = currentNode.entities.IndexOf(currentEdge.Data.ent) + 1;
                    edgeEntityOverride = false;
                    MessageBox.Show("An edge with the same target entity alreadyexists. The edge is:" + System.Environment.NewLine + edges[i].ToString(), "Duplicate Edge", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
            }

            // Do nothing on a -1 change
            if (edgeEntityList.SelectedIndex == -1) return;

            // Here is the logic that dictates that if there is indeed a target, there must be an event other than timed

            // Also includes the logic to set the target entity correctly
            int index = edgeEntityList.SelectedIndex;
            if (index > 0)
            {
                currentEdge.Data.ent = currentNode.entities[edgeEntityList.SelectedIndex - 1];
                defaultType = InputType.InputName.Tap;

                // However, if there is only TIMED or nothing even, then we choose to add Tap
                if (NothingChecked() || (currentEdge.Data.inputTypes.Count == 1 && currentEdge.Data.inputTypes[0].inputName == InputType.InputName.Timed))
                {
                    removeEvent(InputType.InputName.Timed);
                    addEvent(InputType.InputName.Tap);
                }
            }
            else
            {
                // Here, we will always want to JUST have timed
                currentEdge.Data.inputTypes.Clear();
                addEvent(InputType.InputName.Timed);

                defaultType = InputType.InputName.Timed;
                currentEdge.Data.ent = null;
            }

            // Update the GUI
            RefreshEdgeProperties();
        }

        private void destinationNode_SelectedIndexChanged(object sender, EventArgs e)
        {

            // Do nothing on a -1 change
            if (destinationNode.SelectedIndex == -1) return;

            // Check to see if there is an edge already in existance with the same start/end (copy of an edge)
            foreach (Edge<Node, EdgeData> edge in graph.GetEdgesFrom(currentNode))
            {
                if (currentEdge.Start == edge.Start && graph.Nodes[destinationNode.SelectedIndex] == edge.End)
                {
                    // Revert the change
                    destinationNode.SelectedIndex = graph.Nodes.IndexOf(currentEdge.End);

                    // ADD A ERROR MESSAGE, WE HAVE A COPY OF A NODE IN THIS CASE
                    return;
                }
            }

            // Set the edges new destination node
            currentEdge.End = graph.Nodes[destinationNode.SelectedIndex];

            // Update the GUI elements by setting the node again
            setCurrentNode(currentNode);
            setCurrentEdge(currentEdge);

            // Set the defaults for the transition properties if applicable
            if (currentEdge != null && currentEdge.Data.cameraType == Interpolation.InterpolationMode.NONE && currentEdge.End.camera != currentEdge.Start.camera)
            {
                currentEdge.Data.cameraType = Interpolation.InterpolationMode.LINEAR;
                currentEdge.Data.cameraTime = 1;
                RefreshTransitionProperties();
            }

        }

        private void changeLabel(String s)
        {
            label.Text = s;
        }

        private void soundOptions_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Only do this if its enabled and current entity is a sound entity
            if (!soundOptions.Enabled || !(currentEntity is SoundEntity))
                return;

            // Set the state of the sound entity
            switch (this.soundOptions.SelectedIndex)
            {
                case 0:
                    ((SoundEntity)currentEntity).state = SoundEntity.SoundEntityState.PLAYING;
                    break;
                case 1:
                    ((SoundEntity)currentEntity).state = SoundEntity.SoundEntityState.PAUSED;
                    break;
                case 2:
                    ((SoundEntity)currentEntity).state = SoundEntity.SoundEntityState.STOPPED;
                    break;
            }

            changeLabel("Changed " + currentEntity.ToString() + " sound option to " + ((SoundEntity)currentEntity).state);
        }


        // Clears the checks of all the checkboxes while not calling the OnCheckChanged on any of them 
        // NOTE: this is a dirty hack D:
        private void clearAllChecks()
        {
            updateCheckBoxes = false;


            tapEvent.Checked = false;
            tapEvent.CheckState = CheckState.Unchecked;
            doubleTapEvent.Checked = false;
            doubleTapEvent.CheckState = CheckState.Unchecked;
            holdEvent.Checked = false;
            holdEvent.CheckState = CheckState.Unchecked;
            timedEvent.Checked = false;
            timedEvent.CheckState = CheckState.Unchecked;
            swipeEvent.Checked = false;
            swipeEvent.CheckState = CheckState.Unchecked;

            swipeDown.Checked = false;
            swipeDown.CheckState = CheckState.Unchecked;
            swipeUp.Checked = false;
            swipeUp.CheckState = CheckState.Unchecked;
            swipeLeft.Checked = false;
            swipeLeft.CheckState = CheckState.Unchecked;
            swipeRight.Checked = false;
            swipeRight.CheckState = CheckState.Unchecked;

            holdTime.Text = "";
            holdTime.Enabled = false;
            timedEventTime.Text = "";
            timedEventTime.Enabled = false;



            updateCheckBoxes = true;
        }

        private void tapEvent_CheckedChanged(object sender, EventArgs e)
        {
            // Dont do anything if the hacked variable is turned off or if there is no target
            if (!updateCheckBoxes) return;

            // Do not allow the checking to occur if there is no target entity
            if (currentEdge.Data.ent == null)
            {
                updateCheckBoxes = false;
                tapEvent.Checked = false;
                tapEvent.CheckState = CheckState.Unchecked;
                updateCheckBoxes = true;
            }

            // Now, if unchecking it makes it so that we have nothing checked, then do not allow it to be checked
            if (NothingChecked())
            {
                updateCheckBoxes = false;
                removeEvent(InputType.InputName.Tap);
                addEvent(InputType.InputName.Timed);
                updateCheckBoxes = true;
            }
            else
            {
                // If the checkbox is checked, then we must add that type to the edge
                if (tapEvent.Checked)
                {
                    addEvent(InputType.InputName.Tap);
                }
                // If it is NOT checked, then we must remove it
                else
                {
                    removeEvent(InputType.InputName.Tap);
                }
            }

            // Refresh the GUI to reflect these changes
            RefreshEdgeProperties();
        }

        private void swipeEvent_CheckedChanged(object sender, EventArgs e)
        {
            // Dont do anything if the hacked variable is turned off or if there is no target
            if (!updateCheckBoxes) return;

            // Do not allow the checking to occur if there is no target entity
            if (currentEdge.Data.ent == null)
            {
                updateCheckBoxes = false;
                swipeEvent.Checked = false;
                swipeEvent.CheckState = CheckState.Unchecked;
                updateCheckBoxes = true;
            }

            // Now, if unchecking it makes it so that we have nothing checked, then do not allow it to be checked
            if (NothingChecked())
            {
                updateCheckBoxes = false;
                removeEvent(InputType.InputName.SwipeDown);
                removeEvent(InputType.InputName.SwipeUp);
                removeEvent(InputType.InputName.SwipeLeft);
                removeEvent(InputType.InputName.SwipeRight);
                addEvent(defaultType);
                updateCheckBoxes = true;
            }
            else
            {
                // If the checkbox is checked, then we must add that type to the edge
                if (swipeEvent.Checked)
                {
                    // DEFAULT swipe event will be the first one (left)
                    addEvent(InputType.InputName.SwipeLeft);
                }
                // If it is NOT checked, then we must remove it
                else
                {
                    removeEvent(InputType.InputName.SwipeDown);
                    removeEvent(InputType.InputName.SwipeUp);
                    removeEvent(InputType.InputName.SwipeLeft);
                    removeEvent(InputType.InputName.SwipeRight);
                }
            }

            // Refresh the GUI to reflect these changes
            RefreshEdgeProperties();
        }

        private void timedEvent_CheckedChanged(object sender, EventArgs e)
        {
            // Dont do anything if the hacked variable is turned off or if there is no target
            if (!updateCheckBoxes) return;

            // Do not allow the checking to occur if there is no target entity
            if (currentEdge.Data.ent == null)
            {
                updateCheckBoxes = false;
                timedEvent.Checked = false;
                timedEvent.CheckState = CheckState.Unchecked;
                updateCheckBoxes = true;
            }


            // Now, if unchecking it makes it so that we have nothing checked, then do not allow it to be checked
            if (!NothingChecked())
            {
                // If the checkbox is checked, then we must add that type to the edge
                if (timedEvent.Checked)
                {
                    addEvent(InputType.InputName.Timed);
                }
                // If it is NOT checked, then we must remove it
                else
                {
                    removeEvent(InputType.InputName.Timed);
                }
            }
            else
            {
                removeEvent(InputType.InputName.Timed);
                addEvent(defaultType);
            }

            // Make sure that the timed event has its box enabled
            RefreshEdgeProperties();
        }

        private bool NothingChecked()
        {
            return !timedEvent.Checked && !tapEvent.Checked && !doubleTapEvent.Checked && !swipeEvent.Checked && !holdEvent.Checked;
        }

        private void removeEvent(InputType.InputName name)
        {
            for (int i = 0; i < currentEdge.Data.inputTypes.Count; i++)
            {
                InputType.InputName eventName = currentEdge.Data.inputTypes[i].inputName;
                if (eventName == name)
                    currentEdge.Data.inputTypes.RemoveAt(i);
            }
        }

        private void addEvent(InputType.InputName name)
        {
            // Do not add a duplicate to this edge, and stop
            foreach (InputType inputType in currentEdge.Data.inputTypes)
            {
                if (inputType.inputName == name) return;
            }

            // If we didnt have one, then actually remove it
            InputType item = new InputType();
            item.inputName = name;

            if (name == InputType.InputName.Hold)
                item.deltaTime = 2;

            if (name == InputType.InputName.Timed)
                item.deltaTime = 600;

            currentEdge.Data.inputTypes.Add(item);

            RefreshEdgeProperties();
        }

        private void holdEvent_CheckedChanged(object sender, EventArgs e)
        {
            // Dont do anything if the hacked variable is turned off or if there is no target
            if (!updateCheckBoxes) return;

            // Do not allow the checking to occur if there is no target entity
            if (currentEdge.Data.ent == null)
            {
                updateCheckBoxes = false;
                holdEvent.Checked = false;
                holdEvent.CheckState = CheckState.Unchecked;
                updateCheckBoxes = true;
            }

            // Now, if unchecking it makes it so that we have nothing checked, then do not allow it to be checked
            if (NothingChecked())
            {
                updateCheckBoxes = false;
                removeEvent(InputType.InputName.Hold);
                addEvent(defaultType);
                updateCheckBoxes = true;
            }
            else
            {
                // If the checkbox is checked, then we must add that type to the edge
                if (holdEvent.Checked)
                {
                    addEvent(InputType.InputName.Hold);
                }
                // If it is NOT checked, then we must remove it
                else
                {
                    removeEvent(InputType.InputName.Hold);
                }
            }

            // Refresh the GUI to reflect these changes
            RefreshEdgeProperties();
        }

        private void transitionTypeDropDown_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Do nothing if nothing is selected
            if (transitionTypeDropDown.SelectedIndex == -1 || transitionOverride) return;

            // Set the camera transition type to the selected type
            currentEdge.Data.cameraType = (Interpolation.InterpolationMode)((ListItem)transitionTypeDropDown.SelectedItem).Value;
        }

        private void cameraTimeframe_KeyPress(object sender, KeyPressEventArgs e)
        {
            // Check for a naughty character in the KeyDown event.
            if (System.Text.RegularExpressions.Regex.IsMatch(e.KeyChar.ToString(), @"[^0-9^+^\-^\/^\*^\(^\)]"))
            {
                // Stop the character from being entered into the control since it is illegal.
                e.Handled = true;
            }
        }

        private void cameraTimeframe_TextChanged(object sender, EventArgs e)
        {
            // Set the time of the camera transition on this edge to this time
            int tryParse;
            if (Int32.TryParse(this.cameraTimeframe.Text.ToString(), out tryParse))
                currentEdge.Data.cameraTime = tryParse;
        }

        private void doubleTabEvent_CheckedChanged(object sender, EventArgs e)
        {
            // Dont do anything if the hacked variable is turned off or if there is no target
            if (!updateCheckBoxes) return;

            // Do not allow the checking to occur if there is no target entity
            if (currentEdge.Data.ent == null)
            {
                updateCheckBoxes = false;
                doubleTapEvent.Checked = false;
                doubleTapEvent.CheckState = CheckState.Unchecked;
                updateCheckBoxes = true;
            }

            // Now, if unchecking it makes it so that we have nothing checked, then do not allow it to be checked
            if (NothingChecked())
            {
                updateCheckBoxes = false;
                removeEvent(InputType.InputName.DoubleTap);
                addEvent(defaultType);
                updateCheckBoxes = true;
            }
            else
            {
                // If the checkbox is checked, then we must add that type to the edge
                if (this.doubleTapEvent.Checked)
                {
                    addEvent(InputType.InputName.DoubleTap);
                }
                // If it is NOT checked, then we must remove it
                else
                {
                    removeEvent(InputType.InputName.DoubleTap);
                }
            }

            // Refresh the GUI to reflect these changes
            RefreshEdgeProperties();
        }

        private void swipeLeft_CheckedChanged(object sender, EventArgs e)
        {
            // Dont do anything if the hacked variable is turned off or if there is no target
            if (!updateCheckBoxes) return;

            // Do not allow the checking to occur if there is no target entity
            if (currentEdge.Data.ent == null)
            {
                updateCheckBoxes = false;
                swipeLeft.Checked = false;
                swipeLeft.CheckState = CheckState.Unchecked;
                updateCheckBoxes = true;
            }

            // If the checkbox is checked, then we must add that type to the edge
            if (this.swipeLeft.Checked)
            {
                addEvent(InputType.InputName.SwipeLeft);
            }
            // If it is NOT checked, then we must remove it
            else
            {
                removeEvent(InputType.InputName.SwipeLeft);
            }

            // Refresh the GUI to reflect these changes
            RefreshEdgeProperties();
        }

        private void swipeRight_CheckedChanged(object sender, EventArgs e)
        {
            // Dont do anything if the hacked variable is turned off or if there is no target
            if (!updateCheckBoxes) return;

            // Do not allow the checking to occur if there is no target entity
            if (currentEdge.Data.ent == null)
            {
                updateCheckBoxes = false;
                swipeRight.Checked = false;
                swipeRight.CheckState = CheckState.Unchecked;
                updateCheckBoxes = true;
            }

            // If the checkbox is checked, then we must add that type to the edge
            if (this.swipeRight.Checked)
            {
                addEvent(InputType.InputName.SwipeRight);
            }
            // If it is NOT checked, then we must remove it
            else
            {
                removeEvent(InputType.InputName.SwipeRight);
            }

            // Refresh the GUI to reflect these changes
            RefreshEdgeProperties();
        }

        private void swipeUp_CheckedChanged(object sender, EventArgs e)
        {
            // Dont do anything if the hacked variable is turned off or if there is no target
            if (!updateCheckBoxes) return;

            // Do not allow the checking to occur if there is no target entity
            if (currentEdge.Data.ent == null)
            {
                updateCheckBoxes = false;
                swipeUp.Checked = false;
                swipeUp.CheckState = CheckState.Unchecked;
                updateCheckBoxes = true;
            }

            // If the checkbox is checked, then we must add that type to the edge
            if (this.swipeUp.Checked)
            {
                addEvent(InputType.InputName.SwipeUp);
            }
            // If it is NOT checked, then we must remove it
            else
            {
                removeEvent(InputType.InputName.SwipeUp);
            }

            // Refresh the GUI to reflect these changes
            RefreshEdgeProperties();
        }

        private void swipeDown_CheckedChanged(object sender, EventArgs e)
        {
            // Dont do anything if the hacked variable is turned off or if there is no target
            if (!updateCheckBoxes) return;

            // Do not allow the checking to occur if there is no target entity
            if (currentEdge.Data.ent == null)
            {
                updateCheckBoxes = false;
                swipeDown.Checked = false;
                swipeDown.CheckState = CheckState.Unchecked;
                updateCheckBoxes = true;
            }

            // If the checkbox is checked, then we must add that type to the edge
            if (this.swipeDown.Checked)
            {
                addEvent(InputType.InputName.SwipeDown);
            }
            // If it is NOT checked, then we must remove it
            else
            {
                removeEvent(InputType.InputName.SwipeDown);
            }

            // Refresh the GUI to reflect these changes
            RefreshEdgeProperties();
        }

        private bool NoSwipeChecked()
        {
            return !swipeDown.Checked && !swipeUp.Checked && !swipeRight.Checked && !swipeLeft.Checked;
        }

        private void holdTime_KeyPress(object sender, KeyPressEventArgs e)
        {
            // Check for a naughty character in the KeyDown event.
            if (System.Text.RegularExpressions.Regex.IsMatch(e.KeyChar.ToString(), @"[^0-9^+^\-^\/^\*^\(^\)]"))
            {
                // Stop the character from being entered into the control since it is illegal.
                e.Handled = true;
            }
        }


        private void holdTime_TextChanged(object sender, EventArgs e)
        {
            // Do nothing if there is nothing put in
            if (holdTime.Text.Equals("")) return;

            // There was a legitimate keypress, so make the new int the value
            for (int i = 0; i < currentEdge.Data.inputTypes.Count; i++)
            {
                InputType type = currentEdge.Data.inputTypes[i];
                if (type.inputName == InputType.InputName.Hold)
                {
                    int tryParse;
                    if (Int32.TryParse(holdTime.Text.ToString(), out tryParse))
                        type.deltaTime = tryParse;
                }
            }
        }

        private void timedEventTime_KeyPress(object sender, KeyPressEventArgs e)
        {
            // Check for a naughty character in the KeyDown event.
            if (System.Text.RegularExpressions.Regex.IsMatch(e.KeyChar.ToString(), @"[^0-9^+^\-^\/^\*^\(^\)]"))
            {
                // Stop the character from being entered into the control since it is illegal.
                e.Handled = true;
            }
        }

        private void timedEventTime_TextChanged(object sender, EventArgs e)
        {
            // Do nothing if there is nothing put in
            if (timedEventTime.Text.Equals("")) return;

            // There was a legitimate keypress, so make the new int the value
            for (int i = 0; i < currentEdge.Data.inputTypes.Count; i++)
            {
                InputType type = currentEdge.Data.inputTypes[i];
                if (type.inputName == InputType.InputName.Timed)
                {
                    int tryParse;
                    if (Int32.TryParse(timedEventTime.Text.ToString(), out tryParse))
                        type.deltaTime = tryParse;
                }
            }
        }

        // This will resolve any conflicts that the graph editor may have with the current state of the scene editor
        // ie, this will remove edges that have been deemed unacceptable by the new set of entities
        public void Resolve()
        {
            // This string array are the errors we will display
            List<String> errors = new List<String>();
            errors.Add("Node-Entity cleanup:" + System.Environment.NewLine + System.Environment.NewLine);

            // REFRESH THE CAMERA LIST
            cameraDropDown.Items.Clear();
            populateCameraList();

            //TODO: ADD THE ENTITIES THAT ARE NEW TO EVERY NODE
            for (int i = 0; i < utils.entities.Count; i++)
            {
                bool alreadyExists = false;
                List<Entity> entities = graph.Nodes[0].entities;
                Entity inQuestion = utils.entities[i];

                // If this entity doesnt have the same name as any entity in root (essentially, all of them) then add it to every node
                for (int j = 0; j < entities.Count; j++)
                {
                    if (entities[j].name.Equals(inQuestion.name))
                    {
                        alreadyExists = true;
                        break;
                    }
                }

                if (!alreadyExists)
                {
                    for (int j = 0; j < graph.Nodes.Count; j++)
                    {
                        graph.Nodes[j].entities.Add(new Entity(inQuestion));
                    }
                }


            }


            // HANDLE NODES
            // For every node, if it still contains an entity that shouldnt be there, then remove it!
            for (int i = 0; i < graph.Nodes.Count; i++)
            {
                Node n = graph.Nodes[i];
                for (int j = 0; j < n.entities.Count; j++)
                {
                    Entity e1 = n.entities[j];
                    bool entityIsOK = false;
                    foreach (Entity e2 in utils.entities)
                    {
                        if (e1.name.Equals(e2.name))
                        {
                            entityIsOK = true;
                            break;
                        }
                    }

                    if (!entityIsOK)
                    {
                        errors.Add(n.ToString() + " no longer contains " + e1.name + "." + System.Environment.NewLine);
                        n.entities.Remove(e1);
                        i = 0;
                    }
                }
            }

            errors.Add(System.Environment.NewLine + "Node-Edge cleanup:" + System.Environment.NewLine + System.Environment.NewLine);

            // HANDLE EDGES
            // For every edge, check to see if the target entity exists in the current list of entities
            for (int i = 0; i < graph.Edges.Count(); i++)
            {
                Edge<Node, EdgeData> edge = graph.Edges.ToList()[i];
                bool edgeIsValid = false;
                foreach (Entity e in utils.entities)
                {
                    // This edge is OK
                    if (edge.Data.ent == null || e.name.Equals(edge.Data.ent.name))
                    {
                        edgeIsValid = true;
                        break;
                    }
                }

                // If we didnt OK the edge, then delete it!
                if (!edgeIsValid)
                {
                    errors.Add(edge.ToString() + " was removed from " + edge.Start.ToString() + " because " + (edge.Data.ent.name) + " was deleted." + System.Environment.NewLine);
                    graph.RemoveEdge(edge.Start, edge.End);
                    i = 0;
                }
            }

            setCurrentNode(graph.Nodes[0]);
            if (graph.GetEdgesFrom(currentNode).ToList().Count > 0)
                setCurrentEdge(graph.GetEdgesFrom(currentNode).ToList()[0]);
            else
                setCurrentEdge(null);


            // make the error message pop up
            String error = "";
            for (int i = 0; i < errors.Count; i++)
                error += errors[i];


            if (errors.Count > 2)
                MessageBox.Show(error);
        }
    }
}
